rockchip_ops->system_off();
}
+static void
+__dead2 rockchip_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
+{
+ if ((RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) &&
+ (rockchip_ops)) {
+ if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE &&
+ rockchip_ops->sys_pwr_down_wfi)
+ rockchip_ops->sys_pwr_down_wfi(target_state);
+ }
+ psci_power_down_wfi();
+}
+
/*******************************************************************************
* Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
* standard
.pwr_domain_suspend = rockchip_pwr_domain_suspend,
.pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
.pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
+ .pwr_domain_pwr_down_wfi = rockchip_pwr_domain_pwr_down_wfi,
.system_reset = rockchip_system_reset,
.system_off = rockchip_system_poweroff,
.validate_power_state = rockchip_validate_power_state,
#include <pmu_com.h>
#include <pwm.h>
#include <soc.h>
+#include <bl31.h>
DEFINE_BAKERY_LOCK(rockchip_pd_lock);
*
* Also note that these counters can run off the 32 kHz clock or the 24 MHz
* clock. While the 24 MHz clock can give us more precision, it's not always
- * available (like when we turn the oscillator off at sleep time). Current
- * understanding is that counts work like this:
+ * available (like when we turn the oscillator off at sleep time). The
+ * pmu_use_lf (lf: low freq) is available in power mode. Current understanding
+ * is that counts work like this:
* IF (pmu_use_lf == 0) || (power_mode_en == 0)
* use the 24M OSC for counts
* ELSE
}
mmio_setbits_32(PMU_BASE + PMU_PWRDN_CON, BIT(PMU_SCU_B_PWRDWN_EN));
+ /*
+ * Disabling PLLs/PWM/DVFS is approaching WFI which is
+ * the last steps in suspend.
+ */
plls_suspend_prepare();
disable_dvfs_plls();
disable_pwms();
enable_dvfs_plls();
plls_resume_finish();
+ /*
+ * The wakeup status is not cleared by itself, we need to clear it
+ * manually. Otherwise we will alway query some interrupt next time.
+ *
+ * NOTE: If the kernel needs to query this, we might want to stash it
+ * somewhere.
+ */
+ mmio_write_32(PMU_BASE + PMU_WAKEUP_STATUS, 0xffffffff);
+
+ mmio_write_32(PMU_BASE + PMU_WKUP_CFG4, 0x00);
+
mmio_write_32(SGRF_BASE + SGRF_SOC_CON0_1(1),
(cpu_warm_boot_addr >> CPU_BOOT_ADDR_ALIGN) |
CPU_BOOT_ADDR_WMASK);
while (1)
;
}
+static void __dead2 sys_pwr_down_wfi(const psci_power_state_t *target_state)
+{
+ uint32_t wakeup_status;
+
+ /*
+ * Check wakeup status and abort suspend early if we see a wakeup
+ * event.
+ *
+ * NOTE: technically I we're supposed to just execute a wfi here and
+ * we'll either execute a normal suspend/resume or the wfi will be
+ * treated as a no-op if a wake event was present and caused an abort
+ * of the suspend/resume. For some reason that's not happening and if
+ * we execute the wfi while a wake event is pending then the whole
+ * system wedges.
+ *
+ * Until the above is solved this extra check prevents system wedges in
+ * most cases but there is still a small race condition between checking
+ * PMU_WAKEUP_STATUS and executing wfi. If a wake event happens in
+ * there then we will die.
+ */
+ wakeup_status = mmio_read_32(PMU_BASE + PMU_WAKEUP_STATUS);
+ if (wakeup_status) {
+ WARN("early wake, will not enter power mode.\n");
+
+ mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, 0);
+
+ disable_mmu_icache_el3();
+ bl31_warm_entrypoint();
+
+ while (1)
+ ;
+ } else {
+ /* Enter WFI */
+ psci_power_down_wfi();
+ }
+}
static struct rockchip_pm_ops_cb pm_ops = {
.cores_pwr_dm_on = cores_pwr_domain_on,
.sys_pwr_dm_resume = sys_pwr_domain_resume,
.sys_gbl_soft_reset = soc_soft_reset,
.system_off = soc_system_off,
+ .sys_pwr_down_wfi = sys_pwr_down_wfi,
};
void plat_rockchip_pmu_init(void)